﻿#include <stdlib.h>
#include <Windows.h>
#include <adl_sdk.h>
#include <iostream>
#include "timer/time_wheel_scheduler.h"

#define AMDVENDORID (1002)

typedef int (*ADL_MAIN_CONTROL_CREATE)(ADL_MAIN_MALLOC_CALLBACK, int);
typedef int (*ADL_MAIN_CONTROL_DESTROY)();
typedef int (*ADL_ADAPTER_NUMBEROFADAPTERS_GET)(int *);
typedef int (*ADL_ADAPTER_ADAPTERINFO_GET)(LPAdapterInfo, int);
typedef int (*ADL_ADAPTER_ACTIVE_GET)(int, int *);
typedef int (*ADL_OVERDRIVE_CAPS)(int iAdapterIndex, int *iSupported, int *iEnabled, int *iVersion);

// fans
typedef int (*ADL_OVERDRIVE5_FANSPEEDTODEFAULT_SET)(int adapterIndex, int thermalControllerIndex);
typedef int (*ADL_OVERDRIVE5_FANSPEEDINFO_GET)(int iAdapterIndex, int iThermalControllerIndex, ADLFanSpeedInfo *lpFanSpeedInfo);
typedef int (*ADL_OVERDRIVE5_TEMPERATURE_GET)(int iAdapterIndex, int iThermalControllerIndex, ADLTemperature *lpTemperature);
typedef int (*ADL_OVERDRIVE5_FANSPEED_GET)(int iAdapterIndex, int iThermalControllerIndex, ADLFanSpeedValue *lpFanSpeedValue);
typedef int (*ADL_OVERDRIVE5_FANSPEED_SET)(int iAdapterIndex, int iThermalControllerIndex, ADLFanSpeedValue *lpFanSpeedValue);

int adapterId = -1;
HINSTANCE hDLL = nullptr;
bool processRunning = true;

ADL_MAIN_CONTROL_CREATE ADL_Main_Control_Create;
ADL_MAIN_CONTROL_DESTROY ADL_Main_Control_Destroy;
ADL_ADAPTER_NUMBEROFADAPTERS_GET ADL_Adapter_NumberOfAdapters_Get;
ADL_ADAPTER_ADAPTERINFO_GET ADL_Adapter_AdapterInfo_Get;
ADL_ADAPTER_ACTIVE_GET ADL_Adapter_Active_Get;
ADL_OVERDRIVE_CAPS ADL_Overdrive_Caps;

// fans
ADL_OVERDRIVE5_FANSPEEDTODEFAULT_SET ADL_Overdrive5_FanSpeedToDefault_Set;
ADL_OVERDRIVE5_FANSPEEDINFO_GET ADL_Overdrive5_FanSpeedInfo_Get;
ADL_OVERDRIVE5_TEMPERATURE_GET ADL_Overdrive5_Temperature_Get;
ADL_OVERDRIVE5_FANSPEED_GET ADL_Overdrive5_FanSpeed_Get;
ADL_OVERDRIVE5_FANSPEED_SET ADL_Overdrive5_FanSpeed_Set;

int Overdrive5_AutoFanControl();

// Memory allocation function
void *__stdcall ADL_Main_Memory_Alloc(int iSize)
{
    void *lpBuffer = malloc(iSize);
    return lpBuffer;
}

// Optional Memory de-allocation function
void __stdcall ADL_Main_Memory_Free(void **lpBuffer)
{
    if (NULL != *lpBuffer)
    {
        free(*lpBuffer);
        *lpBuffer = NULL;
    }
}

BOOL CtrlHandler(DWORD fdwCtrlType)
{
    switch (fdwCtrlType)
    {
    case CTRL_C_EVENT:
    case CTRL_CLOSE_EVENT:
        processRunning = false;
        return TRUE;
    default:
        return FALSE;
    }
}

int main(int argc, char const *argv[])
{
    SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);

    hDLL = LoadLibrary(L"atiadlxx.dll");
    if (hDLL == nullptr)
    {
        hDLL = LoadLibrary(L"atiadlxy.dll");
    }
    if (hDLL == nullptr)
    {
        std::cout << "ADL library not found!" << std::endl;
        return 0;
    }
    ADL_Main_Control_Create = (ADL_MAIN_CONTROL_CREATE)GetProcAddress(hDLL, "ADL_Main_Control_Create");
    ADL_Main_Control_Destroy = (ADL_MAIN_CONTROL_DESTROY)GetProcAddress(hDLL, "ADL_Main_Control_Destroy");
    ADL_Adapter_NumberOfAdapters_Get = (ADL_ADAPTER_NUMBEROFADAPTERS_GET)GetProcAddress(hDLL, "ADL_Adapter_NumberOfAdapters_Get");
    ADL_Adapter_AdapterInfo_Get = (ADL_ADAPTER_ADAPTERINFO_GET)GetProcAddress(hDLL, "ADL_Adapter_AdapterInfo_Get");
    ADL_Adapter_Active_Get = (ADL_ADAPTER_ACTIVE_GET)GetProcAddress(hDLL, "ADL_Adapter_Active_Get");
    ADL_Overdrive_Caps = (ADL_OVERDRIVE_CAPS)GetProcAddress(hDLL, "ADL_Overdrive_Caps");

    // fans
    ADL_Overdrive5_FanSpeedToDefault_Set = (ADL_OVERDRIVE5_FANSPEEDTODEFAULT_SET)GetProcAddress(hDLL, "ADL_Overdrive5_FanSpeedToDefault_Set");
    ADL_Overdrive5_FanSpeedInfo_Get = (ADL_OVERDRIVE5_FANSPEEDINFO_GET)GetProcAddress(hDLL, "ADL_Overdrive5_FanSpeedInfo_Get");
    ADL_Overdrive5_Temperature_Get = (ADL_OVERDRIVE5_TEMPERATURE_GET)GetProcAddress(hDLL, "ADL_Overdrive5_Temperature_Get");
    ADL_Overdrive5_FanSpeed_Get = (ADL_OVERDRIVE5_FANSPEED_GET)GetProcAddress(hDLL, "ADL_Overdrive5_FanSpeed_Get");
    ADL_Overdrive5_FanSpeed_Set = (ADL_OVERDRIVE5_FANSPEED_SET)GetProcAddress(hDLL, "ADL_Overdrive5_FanSpeed_Set");

    int i = 0;
    int iNumberAdapters = 0;
    int iOverdriveEnabled = 0;
    int iOverdriveVersion = 0;
    int iOverdriveSupported = 0;
    LPAdapterInfo lpAdapterInfo = nullptr;
    if (ADL_OK != ADL_Main_Control_Create(ADL_Main_Memory_Alloc, 1))
    {
        std::cout << "ADL Initialization Error!" << std::endl;
        return 0;
    }
    if (ADL_OK != ADL_Adapter_NumberOfAdapters_Get(&iNumberAdapters))
    {
        std::cout << "Cannot get the number of adapters!" << std::endl;
        return 0;
    }
    if (0 < iNumberAdapters)
    {
        lpAdapterInfo = (LPAdapterInfo)malloc(sizeof(AdapterInfo) * iNumberAdapters);
        memset(lpAdapterInfo, '\0', sizeof(AdapterInfo) * iNumberAdapters);
        ADL_Adapter_AdapterInfo_Get(lpAdapterInfo, sizeof(AdapterInfo) * iNumberAdapters);
    }
    for (i = 0; i < iNumberAdapters; i++)
    {
        int adapterActive = 0;
        AdapterInfo adapterInfo = lpAdapterInfo[i];
        ADL_Adapter_Active_Get(adapterInfo.iAdapterIndex, &adapterActive);
        if (adapterActive && adapterInfo.iVendorID == AMDVENDORID)
        {
            adapterId = adapterInfo.iAdapterIndex;
            break;
        }
    }
    if (-1 == adapterId)
    {
        std::cout << "Cannot find active AMD adapter" << std::endl;
        return 0;
    }
    ADL_Overdrive_Caps(adapterId, &iOverdriveSupported, &iOverdriveEnabled, &iOverdriveVersion);

    uint32_t timer_step_ms = 50;
    // Four level time wheels: Hour, Minute, Secon, Millisecond.
    TimeWheelScheduler timeWheelScheduler(timer_step_ms);
    // Hour time wheel. 24 scales, 1 scale = 60 * 60 * 1000 ms.
    timeWheelScheduler.AppendTimeWheel(24, 60 * 60 * 1000, "HourTimeWheel");
    // Minute time wheel. 60 scales, 1 scale = 60 * 1000 ms.
    timeWheelScheduler.AppendTimeWheel(60, 60 * 1000, "MinuteTimeWheel");
    // Second time wheel. 60 scales, 1 scale = 1000 ms.
    timeWheelScheduler.AppendTimeWheel(60, 1000, "SecondTimeWheel");
    // Millisecond time wheel. 1000/timer_step_ms scales, 1 scale = timer_step ms.
    timeWheelScheduler.AppendTimeWheel(1000 / timer_step_ms, timer_step_ms, "MillisecondTimeWheel");
    timeWheelScheduler.Start();
    auto timer_id = timeWheelScheduler.CreateTimerEvery(5000, Overdrive5_AutoFanControl);
    while (processRunning)
    {
        std::this_thread::sleep_for(std::chrono::microseconds(50));
    }
    timeWheelScheduler.Stop();
    ADL_Main_Control_Destroy();
    return 0;
}

int Overdrive5_AutoFanControl()
{

    ADLFanSpeedInfo fanSpeedInfo = {0};
    fanSpeedInfo.iSize = sizeof(ADLFanSpeedInfo);
    if (ADL_OK != ADL_Overdrive5_FanSpeedInfo_Get(adapterId, 0, &fanSpeedInfo))
    {
        return 0;
    }
    int fanSpeedReportingMethod = 0;
    ADLFanSpeedValue fanSpeedValue = {0};
    fanSpeedReportingMethod = ((fanSpeedInfo.iFlags & ADL_DL_FANCTRL_SUPPORTS_RPM_READ) == ADL_DL_FANCTRL_SUPPORTS_RPM_READ) ? ADL_DL_FANCTRL_SPEED_TYPE_RPM : ADL_DL_FANCTRL_SPEED_TYPE_PERCENT;
    fanSpeedValue.iSpeedType = fanSpeedReportingMethod;
    if (ADL_OK != ADL_Overdrive5_FanSpeed_Get(adapterId, 0, &fanSpeedValue))
    {
        return 0;
    }
    ADLTemperature adlTemperature = {0};
    adlTemperature.iSize = sizeof(ADLTemperature);
    if (ADL_OK != ADL_Overdrive5_Temperature_Get(adapterId, 0, &adlTemperature))
    {
        return 0;
    }
    std::cout << "当前温度:" << (int)(adlTemperature.iTemperature / 1000) << "°, ";
    // // values for ADLFanSpeedValue.iSpeedType
    // // #define ADL_DL_FANCTRL_SPEED_TYPE_PERCENT    1
    // // #define ADL_DL_FANCTRL_SPEED_TYPE_RPM        2
    int maxValue = fanSpeedInfo.iMaxRPM;
    if (fanSpeedReportingMethod == ADL_DL_FANCTRL_SPEED_TYPE_PERCENT)
    {
        maxValue = fanSpeedInfo.iMaxPercent;
    }
    ADLFanSpeedValue newFanSpeed = {0};
    newFanSpeed.iSpeedType = fanSpeedReportingMethod;
    // 判定温度
    if (adlTemperature.iTemperature >= 90000)
    {
        newFanSpeed.iFanSpeed = (int)(maxValue * 0.95);
    }
    else if (adlTemperature.iTemperature >= 80000)
    {
        newFanSpeed.iFanSpeed = (int)(maxValue * 0.9);
    }
    else if (adlTemperature.iTemperature >= 75000)
    {
        newFanSpeed.iFanSpeed = (int)(maxValue * 0.85);
    }
    else if (adlTemperature.iTemperature >= 65000)
    {
        newFanSpeed.iFanSpeed = (int)(maxValue * 0.8);
    }
    else if (adlTemperature.iTemperature >= 45000)
    {
        newFanSpeed.iFanSpeed = (int)(maxValue * 0.35);
    }
    else
    {
        std::cout << "默认温控" << std::endl;
        ADL_Overdrive5_FanSpeedToDefault_Set(adapterId, 0);
        return 0;
    }
    ADL_Overdrive5_FanSpeed_Set(adapterId, 0, &newFanSpeed);
    if (fanSpeedReportingMethod == ADL_DL_FANCTRL_SPEED_TYPE_PERCENT)
    {
        std::cout << "设置风扇转速百分比为:" << newFanSpeed.iFanSpeed << "%/分钟" << std::endl;
    }
    else
    {
        std::cout << "设置风扇转速为:" << newFanSpeed.iFanSpeed << "转/分钟" << std::endl;
    }
    return 0;
}